﻿using GXWA_DATA.Model.ConfigModel;
using GXWA_DATA.Model.Singleton;
using S7.Net;
using SqlSugar;
using GXWA_DATA.Config;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MappingTable = GXWA_DATA.Config.MappingTable;
using GXWA_DATA.Utils;
using GXWA_DATA.Dao;
using DeHeng_Mes.Constant;
using DeHeng_Mes.Model.Boss;
using DeHeng_Mes.Utils;
using DeHeng_Mes.Model.Table;
using GXWA_DATA.Model.ApiModel;
using Azure;
using static Dm.net.buffer.ByteArrayBuffer;
using static System.Collections.Specialized.BitVector32;
using System.Diagnostics;

namespace GXWA_DATA.ThreadTask
{
    public class GatherTask
    {
        #region 成员实例
        Dictionary<string, byte> stationStatusCache;
        DataDao dataDao = new DataDao();
        #endregion

        /// <summary>
        /// 采集数据
        /// </summary>
        public async Task GatherData()
        {
            try
            {
                // 初始化缓存
                stationStatusCache = new Dictionary<string, byte>();

                // 获取工位顺序
                Dictionary<string, int> stations = MappingTable.GetStation();

                while (true)
                {
                    PlcConfigModel? plcConfigModel = PlcConfigModelSingleton.Instance.FirstOrDefault(x => x.Config.Nick == "主线");

                    if (plcConfigModel == null)
                        continue;

                    Plc plc = plcConfigModel.plc;

                    if (plc.IsConnected)
                    {
                        foreach (var station in stations)
                        {
                            byte status = (byte)plc.Read(DataType.DataBlock, 901, station.Value, VarType.Byte, 1);

                            if (stationStatusCache.TryGetValue(station.Key, out byte oldStatus))
                            {
                                // 有缓存比较值
                                if (status != oldStatus)
                                {
                                    Tools.ProduceLogText($"[{station.Key}]执行操作[{status}]");

                                    try
                                    {
                                        // 更新
                                        stationStatusCache[station.Key] = status;

                                        // 执行
                                        Execute(status, station.Key, station.Value, plcConfigModel);
                                    }
                                    catch (Exception ex)
                                    {
                                        Tools.ProduceLogTextError($"[{station.Key}]执行操作[{status}]失败，原因是[{ex.Message}]");
                                    }
                                }
                            }
                            else
                            {
                                // 未缓存则缓存
                                stationStatusCache[station.Key] = status;
                            }
                        }
                    }

                    await Task.Delay(100);
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"数据采集异常，原因是[{ex.Message}]");
            }
        }

        /// <summary>
        /// 执行操作
        /// </summary>
        /// <param name="status"></param>
        private async void Execute(byte status, string station, int index, PlcConfigModel lineMain)
        {
            // 获取夹具编号
            char jar = station[station.Length - 1];

            // 获取当前工位对应PlcName
            string plcName = MappingTable.GetPlcNameByStation(station);

            if (string.IsNullOrEmpty(plcName))
                return;

            // 获取数据偏移量映射
            Dictionary<string, int> dataOff = MappingTable.GetDataOffByStation(plcName);

            PlcConfigModel? plcConfigModel = PlcConfigModelSingleton.Instance.Where(x => x.Config.Name.Equals(plcName)).FirstOrDefault();

            if (plcConfigModel == null || !plcConfigModel.plc.IsConnected)
                return;

            // 获取二维码
            string traceId = plcConfigModel.plc.Read(DataType.DataBlock, 900, dataOff[$"Code_{jar}"], VarType.S7String, 256).ToString().Trim();

            // 获取工单
            OrderStatus? orderStatus = OrderSingleton.Instance.FirstOrDefault();

            // 获取执行时间
            DateTime now = DateTime.Now;

            try
            {
                switch (status)
                {
                    // 入站操作
                    case 1:
                        // 弧焊数据采集线程启动
                        _ = Task.Run(() =>
                        {
                            WeldDataCollection(dataOff, traceId, station, plcConfigModel, jar);
                        });

                        // 工位入站
                        dataDao.SaveStationData(new PartProcess()
                        {
                            StartTime = now,
                            Station = station,
                            TraceId = traceId,
                        });

                        // 工单绑定工件
                        if (orderStatus == null)
                            return;

                        dataDao.SaveTraceId(new OrderTrace()
                        {
                            TraceId = traceId,
                            OrderId = orderStatus.OrderId,
                            CreateTime = now,
                        });

                        // 首工位上件推送
                        if (station.Contains("Op10"))
                        {
                            await PutMes.LineMesToMes(new PartPutApiModel()
                            {
                                LineName = orderStatus.LineName,
                                OrderCode = orderStatus.OrderId,
                                CrafrCode = station,
                                StartTime = now,
                            }, SystemConstant.PART_START_PUT, $"[{orderStatus.OrderId}]工件上件任务推送，追溯码[{traceId}]");
                        }

                        break;
                    // 出站操作
                    case 2:
                        // 初始化结果
                        string result = "OK";

                        // 激光切割数据采集

                        // 工位出站
                        UpdateStationEndTime(traceId, station, result, now);

                        if (orderStatus == null)
                            return;

                        int productionNum = 0;

                        // 更新产量
                        if(result.Equals("NG"))
                        {
                            productionNum = ProductionSingleton.AddNgProduction(station, orderStatus.OrderId);
                        }

                        int productionNg = ProductionSingleton.AddProduction(station, orderStatus.OrderId);

                        // 工序报工
                        await PutMes.LineMesToMes(new StationPutApiModel()
                        {
                            LineName = orderStatus.LineName,
                            OrderCode = orderStatus.OrderId,
                            CrafrCode = station,
                            EndTime = now.ToString("yyyy-MM-dd HH:mm:ss"),
                            ProduceNg = productionNg,
                            ProduceNum = productionNum
                        }, SystemConstant.STATION_PUT, $"[{orderStatus.OrderId}]工序报工任务推送，追溯码[{traceId}]");

                        break;
                }
            }
            catch (Exception ex)
            {
                Tools.ProduceLogTextError($"[{station}]执行[{status}]操作失败，原因是[{ex.Message}]");
            }
            finally
            {
                Tools.ProduceLogText($"[{station}]执行[{status}]操作，追溯码[{traceId}]");

                // 出站反馈
                lineMain.plc.Write(DataType.DataBlock, 902, index, status);
            }
        }

        /// <summary>
        /// 更新出站时间
        /// </summary>
        /// <param name="traceId"></param>
        /// <param name="station"></param>
        /// <param name="result"></param>
        /// <param name="now"></param>
        private void UpdateStationEndTime(string traceId, string station, string result, DateTime now)
        {
            List<PartProcess> partProcesses = dataDao.GetStationData(traceId, station);

            if (partProcesses.Count == 0)
            {
                StationEndSave(traceId, station, result, now);
            }
            else
            {
                PartProcess partProcess = partProcesses.OrderByDescending(x => x.Id).First();

                // 判断是否有结束时间，有则新插入，无则更新
                if (Tools.IsDateTimeValid(partProcess?.EndTime))
                {
                    dataDao.UpdateEndTime(partProcess.Id, now, result, traceId);

                    Tools.ProduceLogText($"[{station}]触发出站更新，出站时间[{now}]，追溯码[{traceId}]");
                }
                else
                {
                    StationEndSave(traceId, station, result, now);
                }
            }
        }

        /// <summary>
        /// 出站存储
        /// </summary>
        private void StationEndSave(string traceId, string station, string result, DateTime now)
        {
            dataDao.SaveStationData(new PartProcess()
            {
                TraceId = traceId,
                Station = station,
                EndTime = now,
                StartTime = now.AddMinutes(-1),
                Result = result
            });

            Tools.ProduceLogTextError($"[{station}]触发出站存储，出站时间[{now}]，追溯码[{traceId}]");
        }

        /// <summary>
        /// 弧焊数据采集线程
        /// </summary>
        private async void WeldDataCollection(Dictionary<string, int> dataOff, string traceId, string station, PlcConfigModel plcConfigModel, char jar)
        {
            Tools.ProduceLogText($"[{station}]弧焊数据采集中");

            List<string> weldIds = new List<string>();

            List<string> weldIs = new List<string>();

            List<string> weldUs = new List<string>();

            List<string> weldSpeeds = new List<string>();

            int count = 0;

            while (true)
            {
                // 读取焊接状态
                short status = Convert.ToInt16(plcConfigModel.plc.Read(DataType.DataBlock, 900, dataOff[$"WeldStatus_{jar}"], VarType.Int, 1));

                if (status != 1)
                {
                    if (weldIds.Count > 0)
                        break;

                    continue;
                }

                if (count > 300)
                    break;

                // 读取焊接代码
                short weldId = (short)plcConfigModel.plc.Read(DataType.DataBlock, 900, dataOff[$"WeldPoint_{jar}"], VarType.Int, 1);

                // 读取焊接电流
                float weldI = (float)plcConfigModel.plc.Read(DataType.DataBlock, 900, dataOff[$"WeldI_{jar}"], VarType.Real, 1);

                // 读取焊接电压
                float weldU = (float)plcConfigModel.plc.Read(DataType.DataBlock, 900, dataOff[$"WeldU_{jar}"], VarType.Real, 1);

                // 读取焊接速度
                float weldSpeed = (float)plcConfigModel.plc.Read(DataType.DataBlock, 900, dataOff[$"WeldSpeed_{jar}"], VarType.Real, 1);

                weldIds.Add(weldId.ToString());
                weldIs.Add(weldI.ToString());
                weldUs.Add(weldU.ToString());
                weldSpeeds.Add(weldSpeed.ToString());

                count++;

                await Task.Delay(200);
            }

            // 存储数据
            string weldIdStr = string.Join(",", weldIds);
            string weldIStr = string.Join(",", weldIs);
            string weldUStr = string.Join(",", weldUs);
            string weldSpeedStr = string.Join(",", weldSpeeds);

            dataDao.SaveWeldData(new TraceWeld()
            {
                SeamNo = weldIdStr,
                Station = station,
                TraceId = traceId,
                WeldI = weldIStr,
                WeldU = weldUStr,
                WireSpeed = weldSpeedStr,
            });

            Tools.ProduceLogText($"[{station}]存储了[{weldIds.Count}]条弧焊数据");
        }
    }
}
